#pragma once

#include <stdint.h>
#include <ostream>


namespace timeutil
{

class TimeInDay
{
public:
    using Hour = int;
    using Minute = int;
    using Second = int;
    using MilliSecond = int;

    TimeInDay(Hour h, Minute m, Second s, MilliSecond ms)
        : h_(h), m_(m), s_(s), ms_(ms) {
    }

    Hour h() const {
        return h_;
    }

    Minute m() const {
        return m_;
    }

    Second s() const {
        return s_;
    }

    MilliSecond ms() const {
        return ms_;
    }

private:
    Hour h_ = 0;
    Minute m_ = 0;
    Second s_ = 0;
    MilliSecond ms_ = 0;
};

class Date
{
public:
    using Year = int;
    using Month = int;
    using Day = int;

    Date(Year y, Month m, Day d)
        : y_(y), m_(m), d_(d) {
    }

    Year y() const {
        return y_;
    }

    Month m() const {
        return m_;
    }

    Day d() const {
        return d_;
    }

private:
    Year y_ = 0;
    Month m_ = 0;
    Day d_ = 0;
};

class DateTime
{
public:
    DateTime(const Date &date, const TimeInDay &time)
        : date_(date), time_(time) {
    }

    static DateTime now();

    Date::Year year() const {
        return date_.y();
    }

    Date::Month month() const {
        return date_.m();
    }

    Date::Day day() const {
        return date_.d();
    }

    TimeInDay::Hour hour() const {
        return time_.h();
    }

    TimeInDay::Minute min() const {
        return time_.m();
    }

    TimeInDay::Second sec() const {
        return time_.s();
    }

    TimeInDay::MilliSecond ms() const {
        return time_.ms();
    }

    class PrintOpt
    {
    public:
        char dateSep() const { return dateSep_; }
        void setDateSep(char c) { dateSep_ = c; }
        char dateTimeSep() const { return dateTimeSep_; }
        void setDateTimeSep(char c) { dateTimeSep_ = c; }
        char timeSep() const { return timeSep_; }
        void setTimeSep(char c) { timeSep_ = c; }
        char milliSecSep() const { return milliSecSep_; }
        void setMilliSecSep(char c) { milliSecSep_ = c; }

    private:
        char dateSep_ = '-';
        char dateTimeSep_ = ' ';
        char timeSep_ = ':';
        char milliSecSep_ = '.';
    };

    void print(std::ostream &out, const PrintOpt &opt) const;

    void print(std::ostream &out) const;

private:
    Date date_;
    TimeInDay time_;

    friend std::ostream &operator<<(std::ostream &out, const DateTime &obj) {
        obj.print(out);
        return out;
    }
};

} // namespace timeutil


